#ifndef XRAR_HPP
#define XRAR_HPP

#define STRICT
#include <windows.h>
#include <stdio.h>
#include <ctype.h>
#include <locale.h>
#include "unrar.h"

class xrar
{
protected:
	static int CALLBACK CallbackProc(UINT msg, LPARAM UserData, LPARAM P1, LPARAM P2)
	{
		switch (msg)
		{
		case UCM_CHANGEVOLUMEW:
			if (P2 == RAR_VOL_ASK)
			{
				printf("\n\nVolume %S is required\nPossible options:\n", (wchar_t *)P1);
				printf("\nEnter - try again");
				printf("\n'R'   - specify a new volume name");
				printf("\n'Q'   - quit");
				printf("\nEnter your choice: ");
				switch (toupper(getchar()))
				{
				case 'Q':
					return(-1);
				case 'R':
				{
					wchar_t *eol;
					printf("\nEnter new name: ");
					fflush(stdin);

					// fgetws may fail to read non-English characters from stdin
					// in some compilers. In this case use something more
					// appropriate for Unicode input.
					fgetws((wchar_t *)P1, MAX_PATH, stdin);

					eol = wcspbrk((wchar_t *)P1, L"\r\n");
					if (eol != NULL)
						*eol = 0;
				}
				return(1);
				default:
					return(1);
				}
			}
			if (P2 == RAR_VOL_NOTIFY)
				printf("\n  ... volume %S\n", (wchar_t *)P1);
			return(1);
		case UCM_PROCESSDATA:
			return(1);
		case UCM_NEEDPASSWORDW:
		{
			wchar_t *eol;
			printf("\nPassword required: ");

			// fgetws may fail to read non-English characters from stdin
			// in some compilers. In this case use something more appropriate
			// for Unicode input.
			fgetws((wchar_t *)P1, (int)P2, stdin);

			eol = wcspbrk((wchar_t *)P1, L"\r\n");
			if (eol != NULL)
				*eol = 0;
		}
		return(1);
		}
		return(0);
	}

	static void OutError(int Error, wchar_t *ArcName, bool ErrType = false)
	{
		switch (Error)
		{
		case ERAR_NO_MEMORY:
			printf("\nNot enough memory");
			break;
		case ERAR_BAD_DATA:
			printf("\n%S: archive header or data are damaged", ArcName);
			break;
		case ERAR_BAD_ARCHIVE:
			printf("\n%S is not RAR archive", ArcName);
			break;
		case ERAR_UNKNOWN_FORMAT:
			printf("Unknown archive format");
			break;
		case ERAR_EOPEN:
			if (ErrType) // Returned by RARProcessFile.
				printf("Volume open error");
			else
				printf("\nCannot open %S", ArcName);
			break;
		case ERAR_ECREATE:
			printf("File create error");
			break;
		case ERAR_ECLOSE:
			printf("File close error");
			break;
		case ERAR_EREAD:
			printf("Read error");
			break;
		case ERAR_EWRITE:
			printf("Write error");
			break;
		case ERAR_SMALL_BUF:
			printf("Buffer for archive comment is too small, comment truncated");
			break;
		case ERAR_UNKNOWN:
			printf("Unknown error");
			break;
		case ERAR_MISSING_PASSWORD:
			printf("Password for encrypted file or header is not specified");
			break;
		case ERAR_EREFERENCE:
			printf("Cannot open file source for reference record");
			break;
		case ERAR_BAD_PASSWORD:
			printf("Wrong password is specified");
			break;
		}
	}

	static void ShowComment(char *CmtBuf)
	{
		printf("\nComment:\n%s\n", CmtBuf);
	}

	static void ShowArcInfo(unsigned int Flags, wchar_t *ArcName)
	{
		printf("\nArchive %S\n", ArcName);
		printf("\nVolume:\t\t%s", (Flags & 1) ? "yes" : "no");
		printf("\nComment:\t%s", (Flags & 2) ? "yes" : "no");
		printf("\nLocked:\t\t%s", (Flags & 4) ? "yes" : "no");
		printf("\nSolid:\t\t%s", (Flags & 8) ? "yes" : "no");
		printf("\nNew naming:\t%s", (Flags & 16) ? "yes" : "no");
		printf("\nRecovery:\t%s", (Flags & 64) ? "yes" : "no");
		printf("\nEncr.headers:\t%s", (Flags & 128) ? "yes" : "no");
		printf("\nFirst volume:\t%s", (Flags & 256) ? "yes" : "no or older than 3.0");
		printf("\n---------------------------\n");
	}

public:
	static int ExtractArchive(wchar_t *ArcName, wchar_t *outPath)
	{
		setlocale(LC_ALL, NULL);

		HANDLE hArcData;
		int RHCode, PFCode;
		char CmtBuf[16384];
		struct RARHeaderDataEx HeaderData;
		struct RAROpenArchiveDataEx OpenArchiveData;

		memset(&HeaderData, 0, sizeof(HeaderData));
		memset(&OpenArchiveData, 0, sizeof(OpenArchiveData));

		OpenArchiveData.ArcNameW = ArcName;
		OpenArchiveData.ArcName = NULL;
		OpenArchiveData.CmtBuf = CmtBuf;
		OpenArchiveData.CmtBufSize = sizeof(CmtBuf);
		OpenArchiveData.OpenMode = RAR_OM_EXTRACT;
		OpenArchiveData.Callback = CallbackProc;
		hArcData = RAROpenArchiveEx(&OpenArchiveData);

		if (OpenArchiveData.OpenResult != 0)
		{
			OutError(OpenArchiveData.OpenResult, ArcName);
			return -1;
		}

		ShowArcInfo(OpenArchiveData.Flags, ArcName);

		if (OpenArchiveData.CmtState == 1)
			ShowComment(CmtBuf);

		int ret = 0;

		while ((RHCode = RARReadHeaderEx(hArcData, &HeaderData)) == 0)
		{
			printf("\nExtracting %S", HeaderData.FileNameW);
			PFCode = RARProcessFileW(hArcData, RAR_EXTRACT, outPath, NULL);
			if (PFCode == 0)
				printf(" OK");
			else
			{
				OutError(PFCode, ArcName, true);
				ret = 1;
				break;
			}
		}

		OutError(RHCode, ArcName);
		RARCloseArchive(hArcData);

		return ret;
	}
};


#endif
